package easier.app.x.service;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.collection.ArraySet;
import androidx.core.util.Consumer;
import java.util.Set;

/**
 * 一个{@link android.app.Service}的实现,使用{@link android.os.IBinder}获取service实例
 * 用于单进程和{@link android.app.Service}通信,使用{@link #bindService(Context, Class, Consumer)}创建通信,
 * 使用{@link #destroy()}销毁service
 */
public class BinderService extends Service {

    /**
     * 连接通道
     */
    private static final Conn sConn = new Conn();

    @SuppressWarnings( "unchecked" )
    public static < T extends BinderService > void bindService( Context context, Class< T > service, Consumer< T > callBack ) {

        if ( sConn.mService == null ) {
            sConn.add( (Consumer< BinderService >) callBack );
            Intent intent = new Intent( context, service );
            context.startService( intent );
        } else {
            try {
                callBack.accept( (T) sConn.mService );
            } catch ( Exception e ) {
                //nothing
            }
        }
    }

    @Override
    public void onCreate() {

        super.onCreate();

        bindService( new Intent( this, this.getClass() ), sConn, Context.BIND_AUTO_CREATE );
    }

    @Nullable
    @Override
    public IBinder onBind( Intent intent ) {

        return new LocalBinder( this );
    }

    public void destroy() {

        unbindService( sConn );
        sConn.destroy();
        stopSelf();
    }

    /**
     * binder
     */
    private static class LocalBinder extends Binder {

        private final Service mService;

        public LocalBinder( Service service ) {

            mService = service;
        }

        private Service get() {

            return mService;
        }
    }

    /**
     * 通过这个代理
     */
    private static class Conn implements ServiceConnection {

        private BinderService mService;

        private Set< Consumer< BinderService > > mSet;

        private void add( Consumer< BinderService > consumer ) {

            if ( mSet == null ) {
                mSet = new ArraySet<>();
            }
            mSet.add( consumer );
        }

        @Override
        public void onServiceConnected( ComponentName name, IBinder service ) {

            mService = ((BinderService) ((LocalBinder) service).get());

            if ( mSet != null ) {
                for ( Consumer< BinderService > consumer : mSet ) {
                    consumer.accept( mService );
                }
            }

            if ( mSet != null ) {
                mSet.clear();
            }
        }

        @Override
        public void onServiceDisconnected( ComponentName name ) {

            destroy();
        }

        private void destroy() {

            mService = null;
            mSet = null;
        }
    }
}
